from queue import Queue
import time

class TextSwimmer(object):
    """
        弹幕携带对象, 弹幕泳道的游泳者
    """

    def __init__(self, text_widget, msg, start_pos, end_pos):
        """
            游泳者的初始化
        """
        self.text = msg
        self.start_pos = start_pos
        self.end_pos = end_pos
        self.extern_dis = 30
        self.text_content = self.construct_show_text(msg)
        self.text_widget = text_widget.clone()
        self.text_widget.setAnchorPoint(cc.Vec2(0.0, 0.5))
        utils.set_content_rich(self.text_widget, self.text_content)
        shadow_color = utils.color_code_2_rgb('000000', 255)
        self.text_widget.enableShadow(shadow_color, cc.Size(3, -3), False)
        self.text_widget.setVisible(False)
        self.text_size = self.text_widget.getContentSize()
        self.last_move_time = time.time()
        self.interval = 50
        self.is_arrive = False
        self.start_swim()

    def construct_show_text(self, msg):
        """
            构建需要显示的信息
        """
        nick = '[' + msg.get('nick') + ']'
        msg_body = msg.get('msg_body')
        text_str = '#cFDFA02%s#n' % nick
        text_str += '#cFFFFFF%s#n' % msg_body
        return text_str

    def start_swim(self):
        """
            弹幕文本移动, 开始游泳
        """
        self.text_widget.setVisible(True)
        pos_x = self.start_pos.x + self.extern_dis
        self.text_widget.setPosition(cc.Vec2(pos_x, 0))
        self.last_move_time = time.time()

    def hide_msg(self):
        """
            隐藏弹幕
        """
        self.text_widget.setVisible(False)

    def tick(self, speed):
        """
            保持移动
        """
        # 计算移动距离
        now = time.time()
        delta_time = now - self.last_move_time
        dis = delta_time * speed
        self.last_move_time = now

        # 进行移动, 并判定是否结束
        cur_pos = self.text_widget.getPosition()
        cur_pos.x += dis

        if cur_pos.x + self.text_size.width / 2.0 + self.extern_dis < self.end_pos.x:
            self.end_swim()
        else:
            self.text_widget.setPosition(cur_pos)

    def end_swim(self):
        """
            游泳结束
        """
        self.text_widget.setVisible(False)
        self.is_arrive = True

    def is_offshore(self):
        """
            下水的游泳者是否已经离岸, 下一个是否可以下水
        """
        cur_pos = self.text_widget.getPosition()
        text_size = self.text_widget.getContentSize()
        text_width = text_size.width * self.text_widget.getScaleX()
        return cur_pos.x + text_width + self.interval < self.start_pos.x


class SwimLine(object):
    """
        弹幕泳道
    """

    def __init__(self, screen_widget, text_widget, position):
        """
            弹幕泳道
        """
        self.screen_widget = screen_widget
        self.text_widget = text_widget

        self.screen_size = screen_widget.getContentSize()
        self.root_node = ccui.Text.create()
        self.root_node.setAnchorPoint(cc.Vec2(0, 0.5))
        self.screen_widget.addChild(self.root_node)
        self.root_node.setPosition(position)
        self.position = position
        self.start_pos, self.end_pos = self.get_line_pos()
        self.last_text_swimmer = None
        self.text_swimmer_list = []
        self.speed = self.screen_size.width / float(random.uniform(5, 10))

    def add_text_swimmer(self, msg):
        """
            添加游泳者
        """
        # 游泳对象创建
        text_swimmer = TextSwimmer(self.text_widget, msg, self.start_pos, self.end_pos)
        self.text_swimmer_list.append(text_swimmer)
        self.last_text_swimmer = text_swimmer

        # 游泳对象控件的添加
        self.root_node.addChild(text_swimmer.text_widget)

    def remove_text_swimmer(self, text_swimmer):
        """
            移除游泳者
        """
        self.root_node.removeChild(text_swimmer.text_widget)
        self.text_swimmer_list.remove(text_swimmer)
        if self.last_text_swimmer is text_swimmer:
            self.last_text_swimmer = None

    def get_line_pos(self):
        """
            获取应道的开始与结束位置
        """
        x = self.screen_size.width
        y = self.position.y
        return cc.Vec2(x, y), cc.Vec2(0, y)

    def get_cur_speed(self):
        """
            获取泳道的速度
        """
        return -self.speed

    def get_swimmer_num(self):
        """
            获取当前游泳在人的数量
        """
        return len(self.text_swimmer_list)

    def set_last_swimmer(self, text_swimmer):
        """
            设置上一个下水的游泳者
        """
        self.last_text_swimmer = text_swimmer

    def is_swim_line_available(self):
        """
            当前泳道是都可下水
        """
        if not self.last_text_swimmer:
            return True
        return self.last_text_swimmer.is_offshore()

    def hide_msg(self):
        """
            隐藏弹幕
        """
        self.text_swimmer_list = []
        self.last_text_swimmer = None
        self.root_node.removeAllChildren()

    def tick(self):
        """
            泳道的tick
        """
        speed = self.get_cur_speed()

        # 每个游泳者进行游动
        arrive_swimmer_list = []
        for text_swimmer in self.text_swimmer_list:
            text_swimmer.tick(speed)
            if text_swimmer.is_arrive:
                arrive_swimmer_list.append(text_swimmer)

        # 剔除上岸的游泳者
        for text_swimmer in arrive_swimmer_list:
            self.remove_text_swimmer(text_swimmer)



class BarrageManager(object):
    """
        弹幕管理器
    """

    def __init__(self, screen_widget, text_widget, max_line):
        """
            弹幕管理器初始化
            @ screen_widget: 视频屏幕绑定的image控件
            @ open_line: 常开的弹幕通道数
            @ max_line: 弹幕较多时最多开的通道数
        """
        # 弹幕管理器的状态
        self.enable = True

        # 初始化弹幕参数
        self.screen_widget = screen_widget
        self.min_line = max(int(max_line * 0.3), 1)
        self.max_line = max_line
        self.open_line = self.min_line
        self.max_msg_count = 1000
        self.msg_cache = Queue(maxsize=self.max_msg_count)
        self.swim_line_list = []

        # 初始化弹幕通道
        self.screen_size = self.screen_widget.getContentSize()
        self.gap_interval = self.screen_size.height / float(max(max_line, 1))
        for i in range(max_line):
            position = cc.Vec2(0, self.screen_size.height -self.gap_interval* ( i +0.5))
            position.y *= self.screen_widget.getScaleX()
            swim_line = SwimLine(self.screen_widget, text_widget, position)
            self.swim_line_list.append(swim_line)

    def show_msg(self, msg):
        """
            显示弹幕MSG
        """
        if not self.enable:
            return

        if self.msg_cache.full():
            return

        try: self.msg_cache.put(msg, block=False)
        except: pass

    def hide_msg(self):
        """
            隐藏弹幕MSG
        """
        self.msg_cache.queue.clear()
        for swim_line in self.swim_line_list:
            if not swim_line:
                continue
            swim_line.hide_msg()

    def get_available_lines(self):
        """
            获取当前可添加弹幕的弹道, 并进行排序
        """
        # 获得可下水的泳道
        available_lines = []
        for i in xrange(self.open_line):
            swim_line = self.swim_line_list[i]
            if swim_line.is_swim_line_available():
                available_lines.append([swim_line, swim_line.get_swimmer_num()])
        available_lines.sort(key= lambda x: x[1])
        return available_lines

    def put_message_in_line(self, available_lines):
        """
            将弹幕放入弹道进行展示
        """
        # 获得需要下水的消息
        msg_list = []
        for i in xrange(len(available_lines)):
            if self.msg_cache.empty():
                break
            try: msg_list.append(self.msg_cache.get(block=False))
            except: pass

        # 安排消息下水, 松散优先, 相同随机
        for msg in msg_list:
            candidate_line_list = []
            for swim_line in available_lines:
                if swim_line[1] == available_lines[0][1]:
                    candidate_line_list.append(swim_line)
            swim_line_tuple = random.choice(candidate_line_list)
            swim_line_tuple[0].add_text_swimmer(msg)
            available_lines.remove(swim_line_tuple)

    def tick(self):
        """
            弹幕通道的tick
        """
        # 各各泳道的tick
        for swim_line in self.swim_line_list:
            swim_line.tick()

        # 获取可添加弹幕的弹道
        self.open_line = min(self.max_line, max(self.min_line, (self.msg_cache.qsize( ) -1 ) /5 + 1))
        available_lines = self.get_available_lines()

        # 将弹幕放入弹道
        self.put_message_in_line(available_lines)
